// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  implementation of CImplSubmitInfo .
//   $Id$



#include "stdafx.h"
#include "submission.h"

#include "DOM3Core.h"
#include "DOM3ASLS.h"
#include "Vertex.h"
#include <fstream>
#include "xmlstate.h"
#include "model.h"
#include "xformsxformselement.h"
#include "modelui.h"
#include "IUIEvents.h"
#include "FP_ReturnCodes.h"
#include "SubmissionUI.h"
#include "SubmissionExtension.h"
#include <Common/URI/msw/URISchemeUtils.h>
#include <Common/Internet/msw/InternetUtils.h>
#include <Common/FileSystem/msw/FileSystemUtils.h>
#include "ExecuteDispatch.h"
#include "ImplContextualValue.h"
#include <algorithm>
#include "implInstance.h"
#include "ImplBind.h"
#include "Vertex.h"
#include "ImplHeader.h"
#include <Common/ResourceHandling/msw/loadHTMLResource.h>

inline HRESULT setOptionalAttribute(IXMLDOMElement * pEl,BSTR sAttrName,BSTR sAttrVal)
{
	//Sets an attribute iff the value is not empty
	HRESULT hr = S_FALSE;
	if(sAttrVal)
	{
		CComBSTR bsAttrName(sAttrName);
		hr = pEl->setAttribute(sAttrName,CComVariant(sAttrVal));
	}
	return hr;
}


std::wstring::size_type find_nocase(const wchar_t * psStr, const wchar_t * psSub);

std::wstring::size_type find_nocase(const wchar_t * psStr, const wchar_t * psSub) {
	std::wstring::size_type iMatchPos = std::wstring::npos; // npos = -1
	const wchar_t * psCurStr		= psStr;
	const wchar_t * psCurSub		= psSub;
	const wchar_t * psMatchStart	= NULL;

	if(psCurStr && psCurSub) {
		while(*psCurStr != '\0' && *psCurSub != '\0') {
			if(toupper(*psCurStr) == toupper(*psCurSub))
				psMatchStart = psCurStr;
			while(*psCurStr != '\0'&& *psCurSub != '\0' && toupper(*psCurStr) == toupper(*psCurSub)) {
				++psCurStr;
				++psCurSub;
			}
			if(*psCurSub != '\0') {
				psCurSub = psSub;
				psMatchStart = NULL;
			}
			while(*psCurStr != '\0' && toupper(*psCurStr) != toupper(*psCurSub)) {
				++psCurStr;
			}
		}

		if(psMatchStart)
			iMatchPos =  psMatchStart - psStr;
	}

	return iMatchPos;
}


bool IsSuccessResponse(long lStatus)
{
	return ((lStatus >= 200 && lStatus < 300) || lStatus == 304);
}

HRESULT CImplSubmitInfo::serializeURLEncodedForm(IXMLDOMNode *pNode, BSTR *bsAsText,IXmlState * pStateResolver, BSTR * psContentType, CSubmissionExtension * pSubExt) const
{
	HRESULT hr = S_OK;
	if(!pNode)
	{
		return S_OK;
	}
	CComBSTR sBody;
	IXMLDOMNodeList * pNl = NULL;
	BSTR bs = ::SysAllocString(L".//*");
	pNode->selectNodes(bs, &pNl);
	::SysFreeString(bs);
	
	CComBSTR sTemp;
	//allows us to only prepend the separator to 
	//	subsequent nodes.
	bool bFirstNodeSerialized = false;
	CComBSTR sSelfXPath = L".";
	while(SUCCEEDED(pNl->nextNode(&pNode)) && pNode)
	{
		BOOL bIsValid, bIsRelevant;
        CComPtr<IXMLDOMNodeList> p0Nl;
		//This is a mild fudge due to the fact that IXMLState::valid takes
		//	a nodeset of 1.  This is a fact that should be revisited
		pNode->selectNodes(sSelfXPath,&p0Nl);
		pStateResolver->valid(p0Nl,&bIsValid);
		pStateResolver->relevant(p0Nl, &bIsRelevant);
		// Process the node if its valid or the form designer has specified 
		// not to validate using the '@validate' attribute.
		if(bIsValid || !m_bValidate)
		{
			if(bIsRelevant || !m_bRelevant) {
				long l;
				IXMLDOMNodeList * pChildren = NULL;
				//can't use hasChildNodes(), it's crap.
				CComQIPtr<IXMLDOMElement> pEl = pNode;
				if(pEl)
				{
					pEl->normalize();
				}
				
				if(
					SUCCEEDED(pNode->get_childNodes(&pChildren))
					&& SUCCEEDED(pChildren->get_length(&l)) 
					&& l==1
					)
				{
					//only run with elements that have a single text child.
					IXMLDOMNode * pChild = NULL;
					pNode->get_firstChild(&pChild);
					if(pChild)
					{
						DOMNodeType eType = NODE_ELEMENT;
						pChild->get_nodeType(&eType);
						if(eType==NODE_TEXT)
						{
							if(bFirstNodeSerialized)
								sBody += m_sSeparator.m_str;
							else
								bFirstNodeSerialized = true;

							pNode->get_baseName(&sTemp);
							sBody += sTemp;
							sBody +="=";
							pChild->get_text(&sTemp);
							//sBody += sTemp;
							//Don't do this to the whole string, 
							//	or you will encode the ampersands
							sBody.Append(URLEncode(CString(sTemp)));
						}
						::safeRelease(pChild);
					}
				}
				::safeRelease(pChildren); //outside the if, just in case of l !=1;
			}
		}
		else
		{
			hr = FP_E_INVALID_INSTANCE;
			::SetupErrorInfo(hr,0);
			break;
		}
	}

	::safeRelease(pNl);
	*bsAsText = sBody.Detach();
	return hr;
}

/*
	removed function version 158
	HRESULT CImplSubmitInfo::postURLEncodedForm(CXMLRequest * req, IXMLDOMNode *pNode,BSTR sURL)
*/
HRESULT CImplSubmitInfo::httpGET(BSTR sURL, VARIANT * pvtResponse)
{
	HRESULT hr = E_FAIL;

	CImplInstance * pInstance = 0;
	if(m_pInstance)
		pInstance = m_pInstance;
	else
		pInstance = m_pModel->getInstance();

	if(pInstance)
	{
		// Bug 564 get a new dom to load the data into.
		// The document returned by getNewDocumnet() already 
		// has the schemas associated with it.
		VARIANT_BOOL v;
		CComPtr<IXMLDOMDocument> pDoc;
		pInstance->getNewDocument(&pDoc);
		if(pDoc)
		{
			// Load the data
			CComVariant vtURL(sURL);
			if(m_eReplaceType == Text)
			{
				if(::wcsstr(sURL,L"file://") == sURL)
				{
					char * sFileName = BSTRToStr(sURL);
					char * sFileContent = 0;
					hr = xport::ReadFromFileSystem(&(sFileName[7]),reinterpret_cast<void**>(&sFileContent));
					if(SUCCEEDED(hr))
					{
						CComBSTR bsContent(sFileContent);
						pvtResponse->bstrVal = bsContent.Detach();
						pvtResponse->vt = VT_BSTR;
					}
					delete [] sFileName;
					delete [] sFileContent;
				}
				else
				{
					void *pData = 0;
					DWORD dwSize = 0;
					char * pcURL = ::BSTRToStr(sURL);
					xport::ReadFromInternet(pcURL,&pData,dwSize);
					delete [] pcURL;
					CComBSTR bsData = reinterpret_cast<char *>(pData);
					pvtResponse->bstrVal = bsData.Detach();
					pvtResponse->vt = VT_BSTR;
				}
			}
			else
			{
				hr = pDoc->load(vtURL,&v);

				if (FAILED(hr) || v == VARIANT_FALSE) {
					CComPtr<IXMLDOMParseError> pErr;
					CComBSTR sReason;
					pDoc->get_parseError(&pErr);
					if(pErr) {
						pErr->get_reason(&sReason);
					}
					//Report the error
					hr = FP_E_INVALID_INST_DATA;
					SetupErrorInfo(hr, sReason);
				} else {
					hr = pDoc->QueryInterface(IID_IUnknown, (void **) &pvtResponse->punkVal);
					if(SUCCEEDED(hr))
						pvtResponse->vt = VT_UNKNOWN;
				}
			}
		}
	}
	else
	{
		// Haven't got a instance so need to display 
		// error message.
		hr = FP_E_INVALID_INSTANCE;
		SetupErrorInfo(hr, L"");
	}

	return hr;
}

void CImplSubmitInfo::StoreResponse(CComVariant * vtResponseData,bool bSuccess,bool bShouldDataBeReplaced)
{
	m_bLastResponseSuccessful = bSuccess;
	m_bShouldDataBeReplaced = bShouldDataBeReplaced;
	//If the submission is not threaded, call the ResponseReceived routine
	if(vtResponseData && vtResponseData->vt != VT_EMPTY && vtResponseData->vt != VT_NULL)
	{
		OnSubmissionResponseReceived(*vtResponseData);
	}
}

void CImplSubmitInfo::ResolveReplace()
{
	//Resolve the replaceType string to an enumerated value.
	//all is the default.
	if(::isEmpty(m_sReplace) || wcscmp(m_sReplace,L"all")==0)
		m_eReplaceType = All;
	else if(wcscmp(m_sReplace,L"instance")==0)
		m_eReplaceType = Instance;
	else if(wcscmp(m_sReplace,L"none")==0)
		m_eReplaceType = None;
	else if(wcscmp(m_sReplace,L"text")==0)
		m_eReplaceType = Text;
	else
	{
		m_eReplaceType = Error;
	}
}


HRESULT CImplSubmitInfo::submit()
{
	HRESULT hr = S_OK;

	bool bCritsecMethod = false;
	
    if(m_eReplaceType != Error) {
//		if(m_bValidMethod) {
			if(m_pModel) {
				CComBSTR sAction;
				// Within get getSerializedData the m_pIstance property is
				// set as it is dependant of the ref/bind attributes. However 
				// if there is a instance attribute we can change the return 
				// location of the instance.
				CComBSTR sReplaceInstance(m_sReplaceInstance);
				CSubmissionExtension * pSubExt = NULL;
				hr = getSubmissionExtension(&pSubExt);

				if(pSubExt)
					hr = pSubExt->GetReturnInstance(sReplaceInstance);
				// Bug 910 - Check for error with the extension.
				if(SUCCEEDED(hr) && sReplaceInstance) 
				{
					CImplInstance *	pInstance = m_pModel->getInstance(sReplaceInstance);
					if(pInstance)
						m_pInstance = pInstance;
					else { 
						hr = FP_E_INVLDREPINST;
						SetupErrorInfo(hr,sReplaceInstance); 
						FireEvent(EVENT_BINDINGEXCEPT);
						m_pXForms->reportException(hr);
					}
				}
				if(!m_pInstance && !isEmpty(sReplaceInstance))
				{
				
				}
				else
				{
					//Notify the event system that we are starting an event.
					FireEvent(EVENT_SUBMIT);
					
					/*
					* Set the defaults
					*		NO, this should be done at parse time.
					*		Method and replace should be enumerated
					*/

					if(m_pXForms->canContinue()) {
						if(pSubExt) {

							/*
							* [TODO]
							* After getting a fragment, we need to:
							*	validate
							*	Remove nodes that are not 'relevant'
							*	Submit using m_sMethod and m_sMediaType
							*		mediaType = "text/xml" means that the 'output' values are used to produce a doc, and
							*		if it's multiply rooted create a parent called "instanceData"
							*/
		
							CComVariant vtData(reinterpret_cast<BSTR>(0));
							CComBSTR sQueryString, sContentType;
							hr = getSerializedData(&sQueryString, &vtData, &sContentType, pSubExt);




							if(SUCCEEDED(hr)) {

								CComBSTR sResponseData;
								CComVariant vtResponseData;
								
								bool bReplace = true; 
								hr = getResponse(&sQueryString, &vtData, &sContentType, &vtResponseData, &bReplace, pSubExt);
								if(vtResponseData.vt == VT_EMPTY || vtResponseData.vt == VT_NULL)
								{
									bCritsecMethod = true;
								}

								if(m_eReplaceType != None)
								{
									bCritsecMethod = true;
									StoreResponse(&vtResponseData,true,bReplace);
								}
								else if(SUCCEEDED(hr) && !m_bSubmitAsynchronously)
								{
									// HACK: Submissions with @mode="synchronous" and @replace="none" were never
									//       firing xforms-submit-done, so I added this condition. The submission
									//       code is such a mess however, I'm not sure if this is the correct
									//       fix or not. -- Phil Booth, 14-Oct-2008.
									FireEvent(EVENT_SUBMIT_DONE);
								}
							}
						}
					}
					delete pSubExt;
				}
			}
			else
			{
				// Invalid model
				hr = FP_E_INVALID_MODEL;
				SetupErrorInfo(hr, L"");
			}
/*		} else {
			// Bug 650 - Should indicate that they are trying to use a invalid method.
			hr = FP_E_BAD_METHOD;
			SetupErrorInfo(hr,m_pVerbResolver?m_pVerbResolver->m_sValueExpression:m_sMethod); 
		}
*/	} else {
		hr = FP_E_BAD_REPLACE;
		SetupErrorInfo(hr,m_sReplace); 
	}

	// Handle any error here, by displaying error message and
	// firing the xforms-submit-error.
	if(FAILED(hr))
	{
  		StoreResponse(0,false,false);
        FireEvent(EVENT_SUBMIT_ERROR);
	}

	if(!bCritsecMethod)
	{
		LeaveActivatedSection();
	}

	return hr;
}

void CImplSubmitInfo::getInstanceNode(IXMLDOMNode ** pNode)
{
	m_pCurrentNode.CopyTo(pNode);
}

HRESULT CImplSubmitInfo::getSubmissionExtensionElement(IXMLDOMNode ** pExtNode) {
	HRESULT hr = E_FAIL;
	CComBSTR bsMethodName(L"GetExtensionDOM");
	CComQIPtr<IHTMLElement> pMaster;
	hr = m_pXFormsElement->QueryInterface(IID_IHTMLElement,(void**)&pMaster);
	if(SUCCEEDED(hr) && pMaster) {
		DISPID dispid = 0;
		CComVariant vtResult;
		DISPPARAMS params = {0,0,0,0};
		hr = ::InvokeDispatch(pMaster,bsMethodName,&params,&vtResult,dispid,DISPATCH_METHOD,LOCALE_SYSTEM_DEFAULT,0,0);
		if(SUCCEEDED(hr)) {
			if(vtResult.vt == VT_DISPATCH)
			{
				CComQIPtr<IXMLDOMDocument> pExtensionDoc = vtResult.pdispVal;
				if(pExtensionDoc)
				{
					 hr = pExtensionDoc->selectSingleNode(L"*", pExtNode);
					 if(FAILED(hr)) {
						// Change to a meaningful 
						hr = E_FAIL;
						// Invalid xml for xf:extension
					 }
				} else {
					hr = E_FAIL;
					// Invalid xml for xf:extension
				}
			} 
		} else {
			hr = E_FAIL;
			// Error locating xf:extension object 
		}
	} else {
		hr = E_FAIL;
		// Unable to obtain the xf:submission element
	}

	return hr;
}

HRESULT BSTRFromIStream(VARIANT * pvtStream, BSTR * psString);
HRESULT BSTRFromBinary(VARIANT * pvtStream, BSTR * psString);

HRESULT BSTRFromIStream(VARIANT * pvtStream, BSTR * psString)
{
	HRESULT hr = S_OK;

	if(pvtStream && psString) {
		void * pBinaryData = NULL;
		CComPtr<IStream> pStream;
		CComBSTR sData;

		if(pvtStream->vt == VT_UNKNOWN) {
			hr = pvtStream->punkVal->QueryInterface(__uuidof(IStream), (void **) &pStream);
		} else if(pvtStream->vt == VT_DISPATCH) {
			hr = pvtStream->pdispVal->QueryInterface(__uuidof(IStream), (void **) &pStream);
		} else {
			hr =  E_INVALIDARG;
		}

		if(SUCCEEDED(hr) && pStream) {

			hr = S_OK;
		}
	} else {
		hr =  E_INVALIDARG;
	}

	return hr;
}

HRESULT BSTRFromBinary(VARIANT * pvtBinary, BSTR * psString) 
{
	HRESULT hr = S_OK;

	return hr;
}

HRESULT put_text(IXMLDOMNode * pNode,BSTR bsText)
{
	//TODO: expand to work as described in setvalue.
	HRESULT hr = E_POINTER;
	
	if(pNode)
	{
		hr = pNode->put_text(bsText);

		if(false && FAILED(hr))
		{
			CComQIPtr<IXMLDOMDocument> pDoc = pNode;
			if(!pDoc)
				pNode->get_ownerDocument(&pDoc);

			CComPtr<IXMLDOMCDATASection> pCData;
			pDoc->createCDATASection(bsText, &pCData);
			
			CComPtr<IXMLDOMNode> pFirst;
			CComPtr<IXMLDOMNode> pOut;
			pNode->get_firstChild(&pFirst);
			DOMNodeType nType;
			pFirst->get_nodeType(&nType);
			if(pFirst && SUCCEEDED(pFirst->get_nodeType(&nType)) && nType == NODE_TEXT)
			{
				pNode->removeChild(pFirst,&pOut);
				pFirst.Release();
				pNode->get_firstChild(&pFirst);
				pOut.Release();
			}
			
			hr = pNode->insertBefore(pCData,CComVariant(pFirst),&pOut);
		}
	}

	return hr;
}

HRESULT	CImplSubmitInfo::replaceData(VARIANT const vtResponseData)
{
	HRESULT hr = S_FALSE;

	if (m_eReplaceType == All)
	{
		CComBSTR sResponseData;
		FireEvent(EVENT_SUBMIT_DONE, true, false);
		//replace the document in the window.
		hr = replaceAll(sResponseData);
	}//if ( the current document is to be replaced with our returned data )
	else if (m_eReplaceType == Instance)
	{
		/*
		* If the replace value is set to 'instance' 
		*		then we overwrite the current instance
		*		data with the data received
		*/
		//Then reinitialize that model.

		//Get a handle to the instance to replace,
		//	if there no instance was fetched by 
		//	getSerializedData get the default instance

		CImplInstance * pInstance = 0;
		if(m_pInstance)
			pInstance = m_pInstance;
		else
			pInstance = m_pModel->getInstance();

		if(pInstance)
		{
			
			// Bug 564. Get a new document to load the data into.
			// The document returned by the method getNewDocument() 
			// has the schemas added to them.
			CComPtr<IXMLDOMDocument> pDoc;
			pInstance->getNewDocument(&pDoc);

			if(pDoc) 
			{
				// Load the data
				VARIANT_BOOL v;
				if(vtResponseData.vt == VT_BSTR)
				{
					hr = pDoc->loadXML(vtResponseData.bstrVal, &v);
				}
				else
				{
					hr = pDoc->load(vtResponseData, &v);
				}

				if (FAILED(hr) || v == VARIANT_FALSE)
				{
					CComPtr<IXMLDOMParseError> pErr;
					CComBSTR sReason;
					pDoc->get_parseError(&pErr);
					if(pErr) {
						pErr->get_reason(&sReason);
					}
					//Report the error
					hr = FP_E_INVALID_INST_DATA;
					SetupErrorInfo(hr, sReason);
				} 
				else if(m_sTarget)
				{
					CComPtr<IXMLDOMElement> pEl;
					pInstance->GetLiveDOM()->m_pXMLParser->get_documentElement(&pEl);
					CComQIPtr<IXMLDOMNode> pContext = pEl;
					if(pContext)
					{
						CComPtr<IXMLDOMNode> pTargetNode;
						m_pModel->getInstanceNode(m_sTarget,pContext,false,&pTargetNode);
						
						if(pTargetNode)
						{
							CComPtr<IXMLDOMNode> pTargetParent;
							pTargetNode->get_parentNode(&pTargetParent);
							if(pTargetParent)
							{
								CComPtr<IXMLDOMElement> pEl;
								pDoc->get_documentElement(&pEl);
								CComQIPtr<IXMLDOMNode> pInsert(pEl);
								CComPtr<IXMLDOMNode> pNewChild;
								pTargetParent->replaceChild(pInsert,pTargetNode,&pNewChild);
							}
						}
						//If the replace attribute contains the value "text" and the submission response conforms
						//to an XML mediatype (as defined by the content type specifiers in [RFC 3023]) or a text media type
						//(as defined by a content type specifier of text / *), then the response data is encoded as text and 
						//replaces the content of the replacement target node.
/*						if(m_eReplaceType == text)
						{
							CComPtr<IXMLDOMNode> pChildNode;
							pTargetNode->get_firstChild(&pChildNode);

							while(pChildNode)
							{
								CComPtr<IXMLDOMNode> pOldChild;
								pTargetNode->removeChild(pChildNode,&pOldChild);
								pChildNode.Release();
								pTargetNode->get_firstChild(&pChildNode);
							}
							pEl.Release();
							
							pDoc->get_documentElement(&pEl);
							CComQIPtr<IXMLDOMNode> pFirst(pEl);
							CComPtr<IXMLDOMNode> pNewChild;
							hr = pTargetNode->appendChild(pFirst, &pNewChild);
						}
*/
						if (SUCCEEDED(hr))
						{
							hr = m_pModel->initializeSilently();
						}
					}

				}
				else
				{
					// Overwrite the live DOM.
					hr = pInstance->putInstanceDocument(pDoc);
#ifdef _DEBUG
if(false)
{
	CComBSTR sXML;
	pDoc->get_xml(&sXML);
	::MessageBoxW(0,sXML,0,0);
}
#endif
					if (SUCCEEDED(hr))
					{
						hr = m_pModel->initializeSilently();
					}
				}
				
			}
		}
	}
	else if (m_eReplaceType == Text)
	{
		if(m_sTarget)
		{
			CImplInstance * pInstance = 0;
			if(m_pInstance)
				pInstance = m_pInstance;
			else
				pInstance = m_pModel->getInstance();
			CComPtr<IXMLDOMElement> pEl;
			pInstance->GetLiveDOM()->m_pXMLParser->get_documentElement(&pEl);
			CComQIPtr<IXMLDOMNode> pContext = pEl;
			if(pContext)
			{
				CNode * pTargetNode; //TODO: probable leak here - pTargetNode is not released.
				m_pModel->getInstanceNode(m_sTarget,pContext,false,&pTargetNode);
				//If just a string, stick it in
				if(vtResponseData.vt == VT_BSTR)
				{
					hr = put_text(pTargetNode,vtResponseData.bstrVal);
					if(FAILED(hr))
						m_pModel->onBindingException(m_sTarget);
				}
				else
				{
					//Otherwise, it is going to be a stream, 
					//	Quickest and easiest way of loading this is load throught msxml.
					//	and serialise out.  This is not strictly the correct way, as the stream could still contain non-xml data.
					VARIANT_BOOL v;
					CDOM3Document  pDoc;
					
					hr = pDoc.m_pXMLParser->load(vtResponseData, &v);
					if(v)
					{
						CComBSTR bs;
						pDoc.m_pXMLParser->get_xml(&bs);
						hr = put_text(pTargetNode,bs);
						if(FAILED(hr))
							m_pModel->onBindingException(m_sTarget);
					}
					else
					{
						CComPtr<IXMLDOMParseError> pErr;
						pDoc.m_pXMLParser->get_parseError(&pErr);
						CComBSTR sReason, sSrc;
						pErr->get_reason(&sReason);
						pErr->get_srcText(&sSrc);
					}
				}
				if (SUCCEEDED(hr))
				{
					hr = m_pModel->initializeSilently();
				}
			}
		}
		else
		{
			//error
		}
	}
	else if (m_eReplaceType == None)
	{
		//Do nothing
	}

	return hr;
}

void CImplSubmitInfo::GetVerb(CSubmissionExtension* pSubExt,CComBSTR & sMethod)
{
	//official XForms 1.1 mechanism to take precedence.
	if(m_pVerbResolver)
	{
		CComPtr<IXMLDOMNode> pNode(m_pCurrentNode);
		if(!pNode)
			m_pModel->GetDefaultEvaluationContext(&pNode);

		m_pXForms->putXPathIdentityResolver(GetIdResolver());
		m_pVerbResolver->GetValue(m_pModel,pNode,&sMethod);
		m_pXForms->putXPathIdentityResolver(0);
	}
	else
	{
		//Failing that, fall back to the old formSplayer proprietary mechanism
		if(pSubExt)
			pSubExt->GetMethod(sMethod);
		//In the absence of either funky way of resolving this, just go with the simple string.
		if(!sMethod)
			m_sRealMethod.CopyTo(&sMethod);
	}

}


void CImplSubmitInfo::GetResource(CSubmissionExtension* pSubExt,CComBSTR & sResource)
{
	if(m_pResourceNameResolver)
	{
		CComPtr<IXMLDOMNode> pNode(m_pCurrentNode);
		if(!pNode)
			m_pModel->GetDefaultEvaluationContext(&pNode);

		m_pXForms->putXPathIdentityResolver(GetIdResolver());
		m_pResourceNameResolver->GetValue(m_pModel,pNode,&sResource);
		m_pXForms->putXPathIdentityResolver(0);
	}
	else
	{
		if(pSubExt) 
			pSubExt->GetAction(sResource);
		
		if(!sResource)
			m_sAction.CopyTo(&sResource);
	}


}
HRESULT CImplSubmitInfo::getResponse(BSTR * psQueryString, VARIANT * pvtData, BSTR * psContentType, VARIANT * pvtResponse, bool * pbReplaceAfter, CSubmissionExtension * pSubExt)
{
	HRESULT hr = S_FALSE;

	bool bWarn = true;

	//canonicalize the URL
	CComBSTR bsURL;
	CComBSTR sAction;
	GetResource(pSubExt,sAction);

	// Bug 910 - Check for error with the extension.
	if(SUCCEEDED(hr)) {
		//if replace="none" don't bother
		//	getting the data back from any 
		//	internal function either.
		if(m_eReplaceType == None)
		{
			(*pvtResponse).vt = VT_NULL;
			pvtResponse = 0;
		}

		// Bug 1009: Allow cookie: and webapp: protocols in xf:instance and xf:submission.
		char *sResolved = 0;
		hr = xport::ResolveScheme(CW2A(sAction), &sResolved, CW2A(m_pXForms->m_sCurrentURL), true);
		if(sResolved) {
			bsURL = CA2W(sResolved);
			free(sResolved);
			if(hr == S_OK)
				bWarn = false;
		} else
			bsURL.Attach(Relative2AbsolutePath(m_pXForms->m_sCurrentURL, sAction));

		if(m_eSerialisedLocation == url)
		{
			if(psQueryString && *psQueryString && wcslen(*psQueryString) > 0)
			{
				//append the querystring.
				//	first see if ? appears,
				wchar_t * pQM = wcschr(bsURL,L'?');
				if(pQM)
					//if there is a QS already. 
					//	add a new & at the beginning to start a new QS argument
					bsURL.Append(m_sSeparator);
				else
					//if there is no QS, append a question mark.
					bsURL.Append("?");
				//append the form data.
				//We have also deleted the data from
				//the data variable so that we don't need
				//	to perform this check again.
				bsURL += *psQueryString;
			}
		}
			
		// Get the Method for the submission and check
		// whether it has been overwritten by xf:submission
		CComBSTR sMethod;
		GetVerb(pSubExt,sMethod);
		// Bug 910 - Check for error with the extension.
		if(SUCCEEDED(hr)) {
			if (sMethod && _wcsicmp(sMethod, L"get") == 0 ) {
				if(m_eReplaceType == All) {
					// Bug 751 - We don't want to replace for replace="all" submissions.
					*pbReplaceAfter = false;

					//Fire the submitDone event before replacing the document.
					FireEvent(EVENT_SUBMIT_DONE, true, false);
					//use the browser's Navigate system.
					hr = m_pXForms->loadURI(bsURL,CImplActionLoadURI::EXlinkShow::replace);
				} else if(isProtocol(bsURL.m_str, L"file")) {
					//using the file protocol.
					hr =  fileGet(bsURL, pvtResponse, bWarn);
				} else {
					//run with whatever method we have been asked to
					// Obtain the list of headers from the xf:extension
					CComBSTRMap HeaderList;
					CComBSTR sUsername(L"");
					CComBSTR sPassword(L"");
					hr = GetHeaders(pSubExt,HeaderList);
					if(pSubExt) {
						if(SUCCEEDED(hr))
							hr =pSubExt->GetUsername(sUsername);
						if(SUCCEEDED(hr))
							hr = pSubExt->GetPassword(sPassword);
					}
					if(SUCCEEDED(hr))
					{
						// HACK: In Hubbub, I need to suppress the default login prompt for requests that
						//       fail authentication. There were two ways that I could see to go about this:
						//         1. Reinstate the old submission extension code, then provide a username
						//            and password in a submission extension in my form. These are then
						//            passed to XHR and, according to my understanding of the MSDN docs, it
						//            will not display the authentication dialog.
						//         2. Revert to a wininet-based approach in my required case (which happens
						//            to be synchronous, so there would be no external impact on that score,
						//            although I notice that there are asynchronous flags for wininet), and
						//            pass the INTERNET_FLAG_NO_AUTH flag to InternetOpenUrl.
						//        Because I don't know how to go about option 1, it has forced me down the
						//        route of option 2 for now, but I am open-minded if someone can offer me
						//        alternative approaches to this problem. So, the true path through the
						//        following test is what was happening before my change. The else condition
						//        is the hack that I've added for Hubbub. -- Phil Booth, 14-Oct-2008.
						if(m_bAuthenticate || m_bSubmitAsynchronously)
							// Send the data to the URL designated by 'action'
							hr = sendData(bsURL, *psContentType, sMethod, HeaderList, sUsername, sPassword, pvtData, pvtResponse);
						else
							hr = wininetGet(bsURL, HeaderList, *pvtData, pvtResponse);
					}
				}
			}
			else if (sMethod && _wcsicmp(sMethod, L"put") == 0 && isProtocol(bsURL.m_str, L"file"))
			{
				//This is a file put
				hr = filePut(bsURL, pvtData, bWarn);
			}
			else
			{
				
				if(m_eReplaceType == All)
				{
					*pbReplaceAfter = false;
					//Fudging for Daiwa.  Use the browser's 
					//	post system.
					CComPtr<IHTMLDocument2> pDocument; // a valid pointer to a IHTMLDocument-Object
					m_pXForms->GetOwnerDocument(&pDocument);
					if(pDocument)
					{
						CComPtr<IHTMLWindow2> pWindow;
						hr=pDocument->get_parentWindow(&pWindow);
						CComQIPtr<IServiceProvider> pSP(pWindow);

						if(pSP)
						{
							CComPtr<IWebBrowser2> pBrowser;// pointer to receive the result
							hr=pSP->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&pBrowser);

							// We are now using IStream and Array of bytes to send 
							// and receive data so we need to get hold of it.
							BYTE * pData = NULL;
							ULONG len = 0;
							hr = BinaryFromVariant(&pData, &len, pvtData);

							if(SUCCEEDED(hr)) {
								SAFEARRAY *sfArray = ::SafeArrayCreateVector(VT_UI1, 0, len);
								if (sfArray)
								{
									LPSTR sSfArrayData;
									hr = ::SafeArrayAccessData(sfArray, (LPVOID*) &sSfArrayData);

									
									memcpy(sSfArrayData,pData,len);
									hr = ::SafeArrayUnaccessData(sfArray);
									CComVariant vtURL(bsURL);
									CComVariant vtFlags;
									CComVariant vtTarget(L"_self");
									//CComVariant vtData(sData);
									// Bug 575 - Actually use the value provided for the mediatype or
									// the default, which is set by initMehtod() on initalisation 
									// of the xf:submission. 
									CComBSTR sHeaders(L"Content-Type: ");
									// The actual value for the Content-Type is 
									// now performed with the serialization.
									sHeaders += *psContentType;
									sHeaders += L"\r\n";
									// Add additional headers specified by the xf:extension
									hr = AddExtensionHeaders(sHeaders, pSubExt);
									// Bug 910 - Check for error with the extension.
									if(SUCCEEDED(hr)) {
										CComVariant vtHeaders(sHeaders);

										CComVariant vtPostData;
										vtPostData.vt = VT_ARRAY | VT_UI1;
										vtPostData.parray = sfArray;
										hr = pBrowser->Navigate2(&vtURL, &vtFlags,&vtTarget,&vtPostData,&vtHeaders);
									}
									//This causes crashes if destroyed
									//	I suspect that some asynchronous processing tries to use it.
									//	It does not seem to leak, as far as I can see.
									//	::SafeArrayDestroy(sfArray);
								}
								// This also cause a crash.
								delete [] pData;
							}
						}
					}
				}
				else
				{
					//run with whatever method we have been asked to
					// Obtain the list of headers from the xf:extension
					CComBSTRMap HeaderList;
					CComBSTR sUsername(L"");
					CComBSTR sPassword(L"");
					hr = GetHeaders(pSubExt,HeaderList);
					if(pSubExt) {
						if(SUCCEEDED(hr))
							hr =pSubExt->GetUsername(sUsername);
						if(SUCCEEDED(hr))
							hr = pSubExt->GetPassword(sPassword);
					}
					if(SUCCEEDED(hr))
					// Send the data to the URL designated by 'action'
						hr = sendData(bsURL, *psContentType, sMethod, HeaderList, sUsername, sPassword, pvtData, pvtResponse);
				}
			}
		}
	}

	return hr;
}

bool CImplSubmitInfo::isProtocol(wchar_t * pszURL, wchar_t * pszProtocol) {
	bool bMatch = false;
	// Only try and match if we have valid pointers.
    if(pszURL && pszProtocol) {
		// Skip over any leading white space.
		while(isspace(*pszURL))
			pszURL++;

		while(*pszURL != '\0' && *pszProtocol != '\0' && tolower(*pszURL) == tolower(*pszProtocol)) {
			pszURL++;
			pszProtocol++;
		}

		if(*pszURL == ':' && *pszProtocol == '\0')
			bMatch = true;
	}

	return bMatch;
}

HRESULT CImplSubmitInfo::GetHeaders( CSubmissionExtension * pSubExp,CComBSTRMap & map_ResolvedHeaders)
{
	HRESULT hr = S_FALSE;
	if(pSubExp)
		hr = pSubExp->GetHeaders(map_ResolvedHeaders);
	if(!m_Headers.empty())
	{
		m_pXForms->putXPathIdentityResolver(GetIdResolver());
		//July 2006 legacy - could be deleted.
		CMapCComBSTRToContextualValue::iterator i = m_Headers.begin();
		while(i != m_Headers.end())
		{
			CComBSTR s;
			CImplContextualValue * c = i->second;
			c->GetValue(m_pModel,m_pCurrentNode,&s);
			map_ResolvedHeaders[i->first] = s.Detach();
			++i;
		}
		m_pXForms->putXPathIdentityResolver(0);
	}
	if(!m_HeaderObjects.empty())
	{
		m_pXForms->putXPathIdentityResolver(GetIdResolver());
		CListOfHeaderObjects::iterator i = m_HeaderObjects.begin();
		while(i != m_HeaderObjects.end())
		{
			(*i)->GetHeaders(m_pModel,m_pCurrentNode,map_ResolvedHeaders);
			++i;
		}
		m_pXForms->putXPathIdentityResolver(0);
	}

	return hr;
}

void CImplSubmitInfo::AddHeaderObject(CImplHeader* pHeader)
{
	m_HeaderObjects.push_back(pHeader);
}

HRESULT CImplSubmitInfo::AddExtensionHeaders(CComBSTR &sHeaders, CSubmissionExtension * pSubExp) {
	HRESULT hr = S_OK;
	if(pSubExp) {
		CComBSTRMap mHeaders;
		hr = GetHeaders(pSubExp,mHeaders);
		// Bug 910 - Check for error with the extension.
		if(SUCCEEDED(hr) && !mHeaders.empty()) {
			std::wstring sHeadersStr(sHeaders); 
			CComBSTRMap::iterator pCur = mHeaders.begin();
			while(pCur != mHeaders.end()) {
				// If the header name exists in the string. The header name are
				// Case Insensitive and must follow \r\n. If a \r\n is followed by 
				// a space or tab then it is a continuation of the previous header
				std::wstring::size_type iHeaderPos = ::find_nocase(sHeadersStr.c_str(), pCur->first);
				if(iHeaderPos != std::wstring::npos && (!iHeaderPos || (sHeadersStr.at(iHeaderPos - 1) == '\n' && sHeadersStr.at(iHeaderPos - 2) == '\r'))) {
					std::wstring::size_type iCrLfPos = iHeaderPos;
					std::wstring::size_type iLen = sHeadersStr.length();
					while(((iCrLfPos = sHeadersStr.find(L"\r\n", iCrLfPos)) != std::wstring::npos) && iCrLfPos + 3 < iLen && (sHeadersStr.at(iCrLfPos + 3) == ' ' || sHeadersStr.at(iCrLfPos + 3) == '\t'));
					
					if(iCrLfPos == std::wstring::npos)
						iCrLfPos = iLen;
					else 
						iCrLfPos += 2;

					sHeadersStr.replace(iHeaderPos, iCrLfPos, L"");
				}
				sHeadersStr += pCur->first;
				sHeadersStr += L": ";
				if(pCur->second)
					sHeadersStr += pCur->second;
				sHeadersStr += L"\r\n";
				pCur++;
			}
			sHeaders = sHeadersStr.c_str();
		}
	}
	return hr;
}

HRESULT CImplSubmitInfo::serializeXML(IXMLDOMNode * pNode , VARIANT * pvtData, IXmlState * pStateResolver, BSTR * psContentType, CSubmissionExtension * pSubExt)
{
	HRESULT hr = S_OK;
	CComBSTR sContentTypeHeader;
	// Get mediatype and check that it has been 
	// modified by the xf:extension
	CComBSTR sMediaType(m_sMediaType);
	if(pSubExt)
		hr = pSubExt->GetMediaType(sMediaType);
	// Bug 910 - Check for error with the extension.
	if(SUCCEEDED(hr)) {
		// Get mediatype and check that it has been 
		// modified by the xf:extension
		CComBSTR sEncoding(m_sEncoding);
		if(pSubExt)
			hr = pSubExt->GetEncoding(sEncoding);
		// Bug 910 - Check for error with the extension.
		if(SUCCEEDED(hr)) {
			// Build the contenttype header.
			if(sMediaType) {
				sContentTypeHeader = sMediaType;
				if(sEncoding) {
					sContentTypeHeader += L"; charset=";
					sContentTypeHeader += sEncoding;
				}
				*psContentType = sContentTypeHeader.Detach();
			}
			hr = serializeByXSLT("xmlSubmit.xml", pNode, pvtData, pStateResolver, pSubExt);
		}
	}

	return hr;
}


HRESULT CImplSubmitInfo::serializeByXSLT(char * sXSLName, IXMLDOMNode * pNode , VARIANT * pvtData,IXmlState * pStateResolver, CSubmissionExtension * pSubExt) const
{
	HRESULT hr = S_OK;
	if(pStateResolver)
	{
		CComPtr<IXSLTemplate> pXSLTemplate;
		hr = pXSLTemplate.CoCreateInstance(CLSID_XSLTemplate60);
		if(SUCCEEDED(hr))
		{
			CComBSTR sXML;
			xport::loadHTMLResource(::_Module.m_hInst,sXSLName, sXML);
			VARIANT_BOOL b;
			CComPtr<IXMLDOMDocument2> pDOM;
			pDOM.CoCreateInstance(CLSID_FreeThreadedDOMDocument60);
			pDOM->put_async(VARIANT_FALSE);
			{
				CComQIPtr<IXMLDOMDocument3> pDOM2 =pDOM;
				pDOM2->put_resolveExternals(VARIANT_FALSE);
				pDOM2->setProperty(L"AllowXsltScript",CComVariant(true));
			}
			hr = pDOM->loadXML(sXML, &b);
			
			if(SUCCEEDED(hr))
			{
				// Get the mediatype and check that it 
				// hasn't been overwritten the xf:extension
				CComBSTR sMediaType(m_sMediaType);
				if(pSubExt)
					hr = pSubExt->GetMediaType(sMediaType);
				// Bug 910 - Check for error with the extension.
				if(SUCCEEDED(hr)) {
					// Get the Encoding and check that it hasn't been
					// overwritten by the xf:extension
					CComBSTR sEncoding(m_sEncoding);
					if(pSubExt)
						hr = pSubExt->GetEncoding(sEncoding);
					// Bug 910 - Check for error with the extension.
					if(SUCCEEDED(hr)) {
						//Modify <xsl:output />
						CComPtr<IXMLDOMNode> pOutputNode;
						CComBSTR bs(L"SelectionNamespaces");
						pDOM->setProperty(bs, CComVariant(L"xmlns:xsl='http://www.w3.org/1999/XSL/Transform'"));
						bs = L"/*/xsl:output";
						pDOM->selectSingleNode(bs, &pOutputNode);
						if(pOutputNode)
						{
							CComQIPtr<IXMLDOMElement> pOutEl = pOutputNode;
							::setOptionalAttribute(pOutEl,L"media-type",sMediaType);
							::setOptionalAttribute(pOutEl,L"cdata-section-elements",m_sCDATASectionElements);
							::setOptionalAttribute(pOutEl,L"doctype-public",m_sDoctypePublic);
							::setOptionalAttribute(pOutEl,L"doctype-system",m_sDoctypeSystem);
							//boolean values, translate to yes/no 
							::setOptionalAttribute(pOutEl,L"indent",m_bIndent?L"yes":L"no");
							::setOptionalAttribute(pOutEl,L"standalone",m_bStandalone?L"yes":L"no");
							//never show an xml declaration, this will be added later.
							CComBSTR sOmit;
							if(wcsicmp(m_sOmitXMLDeclaration, L"true") == 0)
								sOmit += L"yes";
							else 
								sOmit += L"no";
							::setOptionalAttribute(pOutEl,L"omit-xml-declaration", sOmit);
							//	These two are set elsewhere.
							::setOptionalAttribute(pOutEl,L"version",m_sVersion);
							::setOptionalAttribute(pOutEl,L"encoding",sEncoding);
						}
						
						//TODO: force inclusion of unused namespace prefixes.
					
						hr = pXSLTemplate->putref_stylesheet(pDOM);
						if(SUCCEEDED(hr))
						{
							CComPtr<IXSLProcessor> pProcessor;
							hr = pXSLTemplate->createProcessor(&pProcessor);
							if(SUCCEEDED(hr) && pProcessor)
							{
								CComBSTR sStateResolverNS(L"http://www.x-port.net/xmlstate");
								pProcessor->addObject(pStateResolver,sStateResolverNS);
								hr = pProcessor->put_input(CComVariant(pNode));

								CComVariant vtOutput;
								if(SUCCEEDED(hr))
								{
									// Get the IStream to store the data
									CComPtr<IStream> pStream;
									hr = getIStream(&pStream);
									if(SUCCEEDED(hr) && pStream)
									{
										hr = pStream->QueryInterface(__uuidof(IUnknown), (void **) &pvtData->punkVal);

										if(SUCCEEDED(hr)) {
											// It need to be attached before we do the 
											// query before the transformation so that 
											// the data gets written to the stream.
											pvtData->vt = VT_UNKNOWN;
											hr = pProcessor->put_output(*pvtData);

											if(SUCCEEDED(hr)) {
												VARIANT_BOOL b;
												hr = pProcessor->transform(&b);

												if(SUCCEEDED(hr) && b == VARIANT_TRUE) {
													// Was using this test code to view the 
													// data that had been written
													// Test code
													// HGLOBAL hg = NULL;
													// GetHGlobalFromStream(pStream,&hg);
													// LPVOID output = GlobalLock(hg);
													// GlobalUnlock(hg); 
													// Text code
													
													// We need to reset the file pointer back to the
													// begin so that when the data is read it is read 
													// from the begining not the end.
													ULONG ulRead = 0;
													LARGE_INTEGER liDis;
													liDis.LowPart = liDis.HighPart = 0;
													
													hr = pStream->Seek( liDis, STREAM_SEEK_SET, 0);
												}
											}
										}
									}
								}
								//clean up.
								CComBSTR sNS(L"http://www.x-port.net/xmlstate");
								pProcessor->addObject(0,sNS);
							}
						}
						else if(false)
						{
							CComBSTR s;
							pDOM->get_xml(&s);
							::MessageBoxW(0,s,0,0);
						}
					}
				}
			}
			else
			{
				CComPtr<IXMLDOMParseError> pPE;
				pDOM->get_parseError(&pPE);
				CComBSTR s;
				pPE->get_reason(&s);
				int i = 1;
			}
		}
	}

	if(m_bValidate) {
		VARIANT_BOOL bValid;
		CComQIPtr<IXmlStateManager> pStateManager = pStateResolver;
		if(pStateManager) {
			pStateManager->isValid(&bValid);
			if(bValid == VARIANT_FALSE)
			{
				hr = FP_E_INVALID_INSTANCE;
				::SetupErrorInfo(hr,0);
			}
		}
	}
	return hr;
}

HRESULT CImplSubmitInfo::doInitialize()
{
	return CImplSingleNodeBinding::doInitialize();
}

HRESULT CImplSubmitInfo::fileGet(BSTR sURL, VARIANT *pvtResponse, const bool bWarn)
{
	HRESULT hr = S_FALSE;

	int iResponse = IDYES;
	if(bWarn) {
		CComBSTR sMSGTemplate;
		sMSGTemplate.LoadString(IDS_LOADWARNING);
		CString sMSG;
		sMSG.Format(CString(sMSGTemplate),sURL);
		iResponse = ::MessageBox(0,sMSG,"FormsPlayer Security Warning!",MB_YESNO|MB_ICONINFORMATION);
	}

	if(iResponse == IDYES)
		// Only difference between fileGet() & httpGET(), is that fileGet() 
		// does the security check above.
		hr = httpGET(sURL, pvtResponse);
	else {
		hr = FP_E_USERCANCEL;
		SetupErrorInfo(hr, L"");
	}

	return hr;
}

HRESULT CImplSubmitInfo::filePut(BSTR sURL, VARIANT *pvtBody, const bool bWarn)
{
	HRESULT hr = E_FAIL;
	if(pvtBody->vt == VT_UNKNOWN && pvtBody->punkVal) {
		CComQIPtr<IStream> pStream = pvtBody->punkVal;
		if(pStream) {
			hr = FP_E_USERCANCEL;	
			CUrl oURL;
			oURL.CrackUrl(CString(sURL));
			CString sPath(oURL.GetUrlPath());
			int iResponse = IDYES;
			if(bWarn) {
				CComBSTR sMSGTemplate;
				sMSGTemplate.LoadString(IDS_SAVEWARNING0);
				CStringW sMSG;
				sMSG.Format(CStringW(sMSGTemplate),sPath);
				iResponse = ::MessageBoxW(0,sMSG,L"FormsPlayer Security Warning!",MB_YESNO|MB_ICONEXCLAMATION);
			}
		
			CImplInstance * pInstance = m_pModel->getInstance();
		
			/*
			As we are now treating submitted data as bytes instead of unicode 
			all the time we need to convert the bytes to unicode for this too 
			work.
			if(iResponse == IDCANCEL)
			{
				sMSGTemplate.LoadString(IDS_SAVEWARNING1);
				sMSG.Format(CStringW(sMSGTemplate),sPath,sBody);
				iResponse = ::MessageBoxW(0,sMSG,L"FormsPlayer Security Warning!",MB_YESNO|MB_ICONINFORMATION);
			}
			*/

			if(iResponse == IDYES)
			{
				//open a file with default settings
				//TODO: ensure that this fails gracefully
				//		when open fails.
				std::ofstream f(sPath, std::ios::binary );

				//counterintuitive naming, is_open returns 0 if not open
				if(f.is_open() != 0)
				{					
					ULONG iRead = 0;
					BYTE ucData[1024];
					
					while(SUCCEEDED(pStream->Read(ucData, 1024, &iRead)) && iRead) {
						//write to it
						f.write((char *) &ucData, iRead);
					}
					//close it
					f.close();

					//This does not belong here!
					FireEvent(EVENT_SUBMIT_DONE);
					hr = S_OK;
				} else {
					// Setup error message which is when handled in
					// submit(). 
					hr = FP_E_POST_DATA;
					SetupErrorInfo(hr, L"");
				}
			} else {
				// FP_E_USER_CANCEL
				SetupErrorInfo(hr, L"");
			}
		} else {
			// Invalid argument
			hr = E_FAIL;
		}
	} else {
		// Invalid argumant
		hr = E_FAIL;
	}

	
	return hr;
}

HRESULT CImplSubmitInfo::sendData(BSTR sURL, BSTR sContentType, BSTR sMethod, CComBSTRMap &HeadersList, BSTR sUsername, BSTR sPassword, VARIANT * pvtData, VARIANT * pvtResponse)
{
	HRESULT hr = E_FAIL;  
	delete m_req;
	m_req = new CXMLRequest();
	m_pDataReadyHandler.Release();
	if(m_bSubmitAsynchronously)
	{
		m_pModel->AddAsyncSubmission(this);
		m_pXForms->AddAsyncRunner(this);
		m_pDataReadyHandler.Attach(CSubmissionCallback::CreateSubmissionCallback(this));
		hr = m_req->sendTextAsync(pvtData, sURL, HeadersList, sContentType, sMethod, sUsername, sPassword,m_pDataReadyHandler);
	}
	else
	{
		hr = m_req->sendText(pvtData, sURL, HeadersList, sContentType, sMethod, sUsername,sPassword);
		long lStatus;
		m_req->m_pXMLRequest->get_status(&lStatus);

		if(SUCCEEDED(hr) && IsSuccessResponse(lStatus))
		{
			if(pvtResponse)
			{
				CComBSTR sCT(L"CONTENT-TYPE");
				CComBSTR sContentType;
				m_req->m_pXMLRequest->getResponseHeader(sCT,&sContentType);

				if(wcsstr(sContentType.m_str,L"xml"))
					hr = m_req->m_pXMLRequest->get_responseStream(pvtResponse);
				else
				{
					//TODO: check the status of the response here, and fire the appropriate events.
					CComBSTR sResponse;
					hr = m_req->m_pXMLRequest->get_responseText(&sResponse);
					if(SUCCEEDED(hr))
					{
						pvtResponse->bstrVal = sResponse.Detach();
						pvtResponse->vt = VT_BSTR;
					}
				}
			}
		}
		else
			hr = E_FAIL;
	}

	return hr;
}

//////////////////////////////////////////////////////////////////////
// CSubmitInfo Class
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CImplSubmitInfo::CImplSubmitInfo(CXFormsElementSubmission * pXFEl)
:CImplSingleNodeBinding(reinterpret_cast<CXFormsElementSingleNodeBinding*>(pXFEl))
	,m_sMethod("") // LIM: spec says no default.
	,m_sSeparator(";") 
	,m_sReplace("all")
	,m_sAction()
	//xsl:output equivalents
		,m_sVersion("1.0")
	,m_pInstance(0)
	,m_pElementalRepresentation(0)
	,m_sRealMethod(0)
	,m_eSerialisedLocation(body)
	,m_eSerialisation(xml)
	,m_sOmitXMLDeclaration("false")
	,m_sEncoding("UTF-8")
	,m_bValidMethod(true)
	,m_bValidate(TRUE)
	,m_bRelevant(TRUE)
	,m_bSerialize(TRUE)
	,m_bSubmitAsynchronously(TRUE)
	,m_req(0)
	,m_pResourceNameResolver(0)
	,m_pVerbResolver(0)
	,m_pMethodResolver(0)
	,m_bShouldDataBeReplaced(false)
	,m_bAuthenticate(true)

{
//	m_pXForms = pModel->m_pXForms;
	//submitinfo becomes ready before its parent model.
	m_bisBound = false;
	//should be renamed, as this overrides the singleNodeBinding::m_pModel;
	// NO, the new spec does not allow cross-model binding of submits.	
//	m_pModel = pModel;
	m_sRef = L".";
	::InitializeCriticalSection(&m_RunningCritsec);

}

void ClearHeader(std::pair<CComBSTR,CImplContextualValue*> pVal)
{
	delete pVal.second;
}

void ClearHeaderObj(CImplHeader* pVal)
{
	delete pVal;
}

CImplSubmitInfo::~CImplSubmitInfo()
{
	delete m_req;
	if(m_pXForms)
		m_pXForms->RemoveAsyncRunner(this);

	if(m_pDataReadyHandler)
	{
		//Prevent async data ready handler from calling back after deletion.
		m_pDataReadyHandler->Invoke(1,IID_NULL,LOCALE_SYSTEM_DEFAULT,DISPATCH_METHOD,0,0,0,0);
	}
	::DeleteCriticalSection(&m_RunningCritsec);
	//prevent calling removeControl on delete.
	const_cast<CImplModel *>(m_pModel) = 0;

	::safeRelease(m_pElementalRepresentation);
	// The only time this should get deleted is 
	//	at close-of play.
	m_bInParentContext = false;
	delete m_pResourceNameResolver;
	delete m_pVerbResolver;
	delete m_pMethodResolver;
	std::for_each(m_Headers.begin(),m_Headers.end(),ClearHeader);
	std::for_each(m_HeaderObjects.begin(),m_HeaderObjects.end(),ClearHeaderObj);
	
}

HRESULT CImplSubmitInfo::OnSubmissionResponseReceived(VARIANT vtResponse)
{
	HRESULT hr = replaceData(vtResponse);
	if(SUCCEEDED(hr) && m_eReplaceType != All)
	{
		//  only when replace is not all, 
		//	as this happens after the submit
		//	has done the replacing.
		//	replace=all fires this event elsewhere.

		FireEvent(EVENT_SUBMIT_DONE);
	}
	m_pDataReadyHandler.Release();
	LeaveActivatedSection();

	return hr;
}

HRESULT CImplSubmitInfo::StoreErroneousResponse(CComVariant & vtData)
{
	HRESULT hr = S_FALSE;
	if(!!m_sInstanceError)
	{
		CImplInstance * pInstance;
		m_pXForms->getInstance(m_sInstanceError,pInstance);
		if(pInstance)
		{
			VARIANT_BOOL vtB;
			CDOM3Document * pLiveDOM = pInstance->GetLiveDOM();
			
			if(vtData.vt == VT_UNKNOWN || vtData.vt == VT_DISPATCH)
			{
				//The above variant types shoudl signifiy IStream.
				hr = pLiveDOM->m_pXMLParser->load(vtData,&vtB);
			}
			else if(vtData.vt == VT_BSTR)
			{
				//Create a wrapper for the returned string in the form of <instanceData><![CDATA[]]></instanceData>
				CStringW s;
				s.Format(L"<instanceData><![CDATA[%s]]></instanceData>",vtData.bstrVal);
				CComBSTR bs;
				bs.Attach(s.AllocSysString());
				hr = pLiveDOM->m_pXMLParser->loadXML(bs,&vtB);
				m_pModel->rebuild(true);
			}
#ifdef _DEBUG
			//Easy to step into code for investigating load errors.
			//	It should not be hit.
			if(false && FAILED(hr))
			{
				CComPtr<IXMLDOMParseError> pErr;
				 pLiveDOM->m_pXMLParser->get_parseError(&pErr);		
				 CComBSTR s;
				 pErr->get_reason(&s);
			}
#endif
		}
		else
		{
			//attempting to store the data in a fictional instance is erroneous.
			hr = E_FAIL;
		}
	}
	else
	{
		//An attempt to call this function when no m_sInstanceError is found is wholly unexpected.
		hr = E_UNEXPECTED;
	}
	return hr;
}


HRESULT CImplSubmitInfo::OnSubmissionResponseReceived()
{
	HRESULT hr = E_FAIL;
	if(m_req)
	{
		long lState;
		hr = m_req->m_pXMLRequest->get_readyState(&lState);
		if(SUCCEEDED(hr) && 4 == lState)
		{
			CComVariant vtData;
			if(m_bShouldDataBeReplaced || !!m_sInstanceError)
			{
				//If replacetype is text, then fetching with a stream would be wasteful.
				if(m_eReplaceType != Text)
					hr = m_req->m_pXMLRequest->get_responseStream(&vtData);
				if(FAILED(hr) || m_eReplaceType == Text)
				{
					CComBSTR bsResponse;
					hr = m_req->m_pXMLRequest->get_responseText(&bsResponse);
					if(SUCCEEDED(hr))
					{
						vtData = bsResponse;
					}
				}
			}

			long lStatus;
			m_req->m_pXMLRequest->get_status(&lStatus);
			if(IsSuccessResponse(lStatus))
			{
				EReplace eReplace = m_eReplaceType;
				if(m_eReplaceType == None)
				{
					FireEvent(EVENT_SUBMIT_DONE);
				}
				else if(m_bShouldDataBeReplaced )
				{
					hr = replaceData(vtData);
					if(SUCCEEDED(hr))
					{
						if( eReplace != All)
						{
							//  only when replace is not all, 
							//	as this happens after the submit
							//	has done the replacing.
							//	replace=all fires this event elsewhere.
							FireEvent(EVENT_SUBMIT_DONE);
						}
					}
					else
					{
						FireEvent(EVENT_SUBMIT_ERROR);
					}
				}
				else
				{
					StoreErroneousResponse(vtData);
					FireEvent(EVENT_SUBMIT_ERROR);
				}

				m_pDataReadyHandler.Release();
			}
			else
			{
				StoreErroneousResponse(vtData);
				FireEvent(EVENT_SUBMIT_ERROR);
			}
			if(true)
			{}
			else
			{
				::MessageBox(0,"Not Implemented Yet",0,0);
				hr = E_NOTIMPL;
			}
			m_pDataReadyHandler.Release();
			LeaveActivatedSection();
			m_pModel->RemoveAsyncSubmission(this);

		}
	}
	m_pXForms->RemoveAsyncRunner(this);

	return hr;

}

HRESULT CImplSubmitInfo::onactivate()
{
	HRESULT hr = E_FAIL;

	if(EnterActivatedSection())
	{
		hr = CFormControl::onactivate();
		
		if(SUCCEEDED(hr))
		{
			hr = submit();
		}
	}
	else
	{
//		::MessageBox(0,"failed critsec entry",0,0);
	}

	return hr;
}


void CImplSubmitInfo::AwaitCompletion()
{
	EnterCriticalSection(&m_RunningCritsec);
	LeaveCriticalSection(&m_RunningCritsec);
}


bool CImplSubmitInfo::EnterActivatedSection()
{
	return TryEnterCriticalSection(&m_RunningCritsec) != FALSE;
}

void CImplSubmitInfo::LeaveActivatedSection()
{
	LeaveCriticalSection(&m_RunningCritsec);

}

void CImplSubmitInfo::SetStateResolverParameters(IXmlState * pStateResolver)
{
	// Bug 866 - If the 'validate' attribute is set to false then we're not validing this 
	// document so need to inform the xml state resolver.
	if(!m_bValidate) {
		CComQIPtr<IXmlStateManager> pStateManager = pStateResolver;
		if(pStateManager)
			pStateManager->put_bValidate(VARIANT_FALSE);
	}
	// Bug 867 - If the 'relevant' attribute is set then we don't want to remove the 
	// irrelevant nodes.
	if(!m_bRelevant) {
		CComQIPtr<IXmlStateManager> pStateManager = pStateResolver;
		if(pStateManager)
			pStateManager->put_bRelevant(VARIANT_FALSE);
	}
}

HRESULT CImplSubmitInfo::InnerGetSerializedData(BSTR * pbsURL, BSTR * psContentType,IXmlState * pStateResolver, IXMLDOMNode * pNode, CSubmissionExtension * pSubExt,VARIANT * pvtRetVal)
{
	HRESULT hr = S_FALSE;
	ESerialisation eSerialisation = m_eSerialisation;
	if(pSubExt)
		hr = pSubExt->GetSerialisation(&eSerialisation);
	if(SUCCEEDED(hr)) 
	{
		CComBSTR sData;
		switch(m_eSerialisation)
		{
			case nvpair:
				hr = serializeURLEncodedForm(pNode,pbsURL,pStateResolver, psContentType, pSubExt);
				if(m_eSerialisedLocation == body)
				{
					pvtRetVal->vt = VT_BSTR;
					pvtRetVal->bstrVal = *pbsURL;
					*pbsURL = 0;
				}
				else
				{
					pvtRetVal->vt = VT_EMPTY;
					pvtRetVal->uiVal = 0;
				}
				break;
			case xml:
				hr = serializeXML(pNode, pvtRetVal,pStateResolver, psContentType, pSubExt);
				break;
			case multipartRelated:
				hr = serializeMultipartRelated(pNode, pvtRetVal,pStateResolver, psContentType, pSubExt);
				break;
			case multipartForm:
				hr = serializeMultipartForm(pNode, pvtRetVal,pStateResolver, psContentType, pSubExt);
				break;
			default:
				hr = E_NOTIMPL;
		}
	}
	return hr;
}


HRESULT CImplSubmitInfo::getSerializedData(BSTR * pbsURL, VARIANT * pvtRetVal, BSTR * psContentType, CSubmissionExtension * pSubExt)
{
	HRESULT hr = S_FALSE;
	//if m_bSerialize is set to FALSE, don't bother with all of this, just return S_FALSE.
	
		//Get the requested node.
		CNode * pContext = 0;
		hr = m_pModel->GetDefaultEvaluationContext(&pContext);
		if(SUCCEEDED(hr))
		{
			// Get the Bind value and check that it isn't overwritten
			// by the xf:extension
			CComBSTR sBind(m_sBind);
			if(pSubExt)
				hr = pSubExt->GetInstanceBind(sBind);
			
			if(SUCCEEDED(hr)) {
				CNode* pNode = NULL;
				if(m_sBind)
				{
					CImplBind *pBind = 0;
					m_pXForms->getBind(sBind,pBind);
					if(pBind) {
						CVertex * pVx = pBind->m_instVertices.GetAt(0);
						pVx->m_pInstanceNode->getNode(&pNode);
					}
				}
				else
				{
					// Seeing that there isn't a bind then we need to obtain 
					// the ref and check that it hasn't been overwritten by the 
					// xf:extension
					CComBSTR sRef(m_sRef);
					if(pSubExt)
						hr = pSubExt->GetInstanceRef(sRef);
					
					if(SUCCEEDED(hr)) {
						m_pXForms->putXPathIdentityResolver(GetIdResolver());
						hr = m_pModel->getInstanceNode(sRef, pContext, false, &pNode);
						m_pXForms->putXPathIdentityResolver(0);
					}
				}

				// Since the method implies default serialisation behaviour,
				// we must re-evaluate it before serialisation occurs.
				//                                      -- Phil Booth, 03-Oct-2006.
				// [no relevant bug number since the shift to Drupal from Bugzilla]
				//It looks like this code was in the wrong place, as it was after serialisation

				CComBSTR sMethod;
				pSubExt->GetMethod(sMethod);
				if(m_pMethodResolver && !sMethod && pNode)
				{
					m_pMethodResolver->GetValue(m_pModel,pNode,&sMethod);
				}
				
				if((!!sMethod) && sMethod != m_sMethod) {
					m_sMethod = sMethod;
				}
				initMethod();
				// Check the validity of the document to send.
				if(pNode)
				{
					m_bisBound = true;
					m_pCurrentNode = pNode;
#ifdef _DEBUG
	
					if(false)
					{
						CComBSTR s;
						pNode->get_xml(&s);
						::MessageBoxW(0,s,0,0);

					}
#endif


					if(m_bSerialize)
					{
						//TODO this and the next Serialization will need
						//	to take into account the properties on 
						//	submission that match those on <xsl:output />
						if(pNode)
						{
							//if all is well, serialize it based on the value of type
							//	see http://www.w3.org/TR/xforms/slice11.html#submit-options
							//	for table

							//Get the state resolver
							CComPtr<IXmlState> pStateResolver;
							hr = m_pModel->m_pMasterDependencyList->GetXMLState(&pStateResolver);
							SetStateResolverParameters(pStateResolver);
							if(SUCCEEDED(hr))
							{
								//get the instance to replace (if it is to be replaced)
							
								//KLUDGE: This is hardly transparent behaviour for the function
								//	since nowhere are we informed of this side-effect.
								//	It is also not always needed, so it could be optimised by
								//	calling it after the test for replace="instance"
								//The problem there is that we don't get a handle to this node
								//	outside this function.
								if(!m_pInstance)
									m_pModel->findInstance(pNode,&m_pInstance);

								if(m_bRelevant)
								{
									CComVariant vt;
									//Strip out irrelevant nodes first
									hr = serializeXML(pNode, &vt,pStateResolver, psContentType, pSubExt);
									if(SUCCEEDED(hr))
									{
										//Add the schemata from the original document, to the one
										//	into which the result is to be loaded.
										CComQIPtr<IXMLDOMDocument> pSourceDoc;
										pNode->get_ownerDocument(&pSourceDoc);
										CComQIPtr<IXMLDOMDocument2> pSourceDoc2;
										
										if(pSourceDoc)
										{
											pSourceDoc2 = pSourceDoc;
										}
										else
										{
											pSourceDoc2 = pNode;
										}
										
										CDOM3Document pDoc;
										if(pSourceDoc2)
										{
											CComVariant vtSchemata;
											pSourceDoc2->get_schemas(&vtSchemata);
											CComQIPtr<IXMLDOMDocument2> pTargetDoc = pDoc.m_pXMLParser;
											pTargetDoc->putref_schemas(vtSchemata);
											
										}
										VARIANT_BOOL b = VARIANT_FALSE;
										if(vt.vt == VT_UNKNOWN)
										{
											pDoc.m_pXMLParser->load(vt,&b);
										}
										else if(vt.vt == VT_BSTR)
										{
											pDoc.m_pXMLParser->loadXML(vt.bstrVal,&b);
										}
										if(b)
										{
											//TODO: Check this - it looks leaky
											CNode * pNode;
											//pNode->Release();
											pNode = pDoc.m_pXMLParser;
											pNode->AddRef();
											if(!isValid(pNode))
											{
												pNode->Release();
												pNode = 0;
												hr = FP_E_INVALID_INSTANCE;
												::SetupErrorInfo(hr,0);		
											}
										}
									}
								
									
								}

								if(SUCCEEDED(hr))
								{
									hr = InnerGetSerializedData(pbsURL, psContentType,pStateResolver,pNode,  pSubExt, pvtRetVal);
								}
							}
							

						}
						else
						{
							//it is not an error to try to submit nothing,
							//it should be serialised as an empty string.
							pvtRetVal->uiVal = 0;
							pvtRetVal->vt = VT_EMPTY;
						}
					}
	/*				else
					{
						hr = FP_E_INVALID_INSTANCE;
						::SetupErrorInfo(hr,0);			
					}
	*/			}
				else
				{
					// It is valid not to bind to a node
					pvtRetVal->uiVal = 0;
					pvtRetVal->vt = VT_EMPTY;
				}
				::safeRelease(pNode);
			}
		}
		::safeRelease(pContext);
	
	return hr;
}

bool CImplSubmitInfo::isValid(IXMLDOMNode *pNode)
{
	//	TODO: make this work for the node in question.
	//	rather than the document, as we may not be sending
	//	a whole document.
	//	also make this run using  XForms validation,
	//	so that XF constraints can be included, 
	//	and formcontrols can be highlited as invalid.
	bool bIsValid = false;
	if(pNode && m_bValidate == TRUE)
	{
		CComPtr<IXMLDOMDocument> pDoc;
		HRESULT	hr = pNode->get_ownerDocument(&pDoc);
		if(SUCCEEDED(hr) && !pDoc)
		{
			//If pNode is a document, it has no ownerDocument
			//	query the node to find out if that is so.
			hr = pNode->QueryInterface(IID_IXMLDOMDocument,(void**)&pDoc);
		}

		CComQIPtr<IXMLDOMDocument2> pDoc2 = pDoc;
		if(pDoc2)
		{
			//call validate
			CComPtr<IXMLDOMParseError> pPE;
			pDoc2->validate(&pPE);
			//check the parseError value, to see if
			//	it failed for validity reasons.
			if(pPE)
			{
				long lErr;
				pPE->get_errorCode(&lErr);
				if(lErr == 0 || lErr == 0xC00CE224 || lErr == -2146697210)
				{
					//0 = Success;
					//0xC00CE224 = no schema to validate by.
					//-2146697210 = could not find the schema
					bIsValid = true; 
				}
				else
				{
					CComBSTR sReason;
					pPE->get_reason(&sReason);
					CString sError;
					sError.Format("document invalid, reason: %S",sReason);
					m_pModel->m_pXForms->reportError(sError);
					
				}
			}
			else
			{
				//No parseError may mean that no error was encountered
				//	I doubt if this ever occurs.
				bIsValid = true;
			}
		}
	}
	else
	{
		//Not strictly the case, but you can't validate nothing.
		//	the only thing we need to watch for is xf:required
		//	statments.  The good thing is, that we don't even
		//	need to evaluate them properly in htis situation,
		//	If any come up as true, fail immediately.
		bIsValid = true;
	}

	return bIsValid;
}

HRESULT CImplSubmitInfo::serializeMultipartRelated(IXMLDOMNode * pNode, VARIANT * pvtData,IXmlState * pStateResolver, BSTR * psContentType, CSubmissionExtension * pSubExt) const 
{
	CComBSTR sContentTypeHeader, sBoundary(L"f93dcbA3"), sType(L"application/xml");
	// The Content-Type header should specify "Multipart/Related" 
	// the m_sMediaType can be change to anything to reflect the
	// content-type of the root data block.
	sContentTypeHeader = L"Multipart/Related";
	// boundary parameter
	sContentTypeHeader += L"; boundary=";
	sContentTypeHeader += sBoundary;
	// type parameter
	sContentTypeHeader += L"; type=";
	sContentTypeHeader += sType;
	// start parameter
	sContentTypeHeader += L"; start=\"<";
	sContentTypeHeader += L"body.";
	CComPtr<IXMLDOMNodeList> pNL;
	pNode->selectNodes(L"/|*|@*", &pNL);
	if(pNL) {
		long lCount = 0;
		pNL->get_length(&lCount);
		wchar_t sCount[20];
		_ltow(lCount, sCount, 10);
		sContentTypeHeader += sCount;
	}
	sContentTypeHeader += "@x-port.net>\"";
				
	*psContentType = sContentTypeHeader.Detach();
	
	return  serializeByXSLT("multipart-mime.xml",pNode,pvtData,pStateResolver, pSubExt);
}

HRESULT CImplSubmitInfo::serializeMultipartForm(IXMLDOMNode * pNode, VARIANT * pvtData, IXmlState * pStateResolver, BSTR * psContentType, CSubmissionExtension * pSubExt) const 
{
	CComBSTR sContentTypeHeader, sBoundary(L"AaB03x");
	// Get the mediatype and check that it hasn't been 
	// overwritten by xf:extension
	CComBSTR sMediaType(m_sMediaType);
	if(pSubExt)
		pSubExt->GetMediaType(sMediaType);
	if(sMediaType) {
		sContentTypeHeader = sMediaType;
		sContentTypeHeader += L"; boundary=";
		sContentTypeHeader += sBoundary;

		*psContentType = sContentTypeHeader.Detach();
	}
	return  serializeByXSLT("multipart-form.xml", pNode, pvtData, pStateResolver, pSubExt);
}

HRESULT CImplSubmitInfo::init(CImpl * pParent)
{
	//Removed ID requirement (see http://skimstone.x-port.net/node/512)
	if(m_pXForms)
		m_pXForms->AddSubmission(this);

	HRESULT hr =  __super::init(pParent);
	if(SUCCEEDED(hr))
	{
		//not setModel, we don't want to be bound to it and constantly updated.
		const_cast<CImplModel *>(m_pModel) = dynamic_cast<CImplModel*>(m_pParentImpl);
	}
	ResolveReplace();

	// Need to initialise the method of the xf:submission early so that the
	// xf:submission can be sent on the early events such as xforms-model-construct-error
	// and xforms-ready. Fixes bug 348
	//initMethod();

	return hr;
}


HRESULT CImplSubmitInfo::replaceAll(CComBSTR sNewDoc)
{
	HRESULT hr = S_OK;
	//Time for a grubby hack, inspired by MS KB problem Q272760
	// namely document.write from Automation Client May Change URL to about:blank
	// May change my left buttock! Does change more like.
	try
	{
		CComPtr<IHTMLDocument2> pDoc2;
		m_pXForms->GetOwnerDocument(&pDoc2);

		if(pDoc2)
		{
			CComPtr<IHTMLWindow2> pWin;
			pDoc2->get_parentWindow(&pWin);
			if(pWin)
			{
				//Add the replaceAll function to the document.
				//	It doesn't matter much if there already is one, it will be overwritten.
				//	The only problem will come if the form designer has 
				//	 called his own replaceAll function from window::onunload
				CComVariant vtR; 
				CComBSTR sScr(L"function replaceAll(s){document.clear();document.open();document.write(s);document.close();}");
				CComBSTR sLang(L"JScript");
				//Note, this cannot be done by insertAdjacentHTML, MSHTML does not parse the script.
				hr = pWin->execScript(sScr.Copy(),sLang.Copy(),&vtR);

				//Get hold of the document's script as IDispatch.
				CComQIPtr<IHTMLDocument> pDoc0(pDoc2);
				if(pDoc0 && SUCCEEDED(hr)) {
					IDispatchPtr pDisp;
					pDoc0->get_Script(&pDisp);
					if(pDisp)
					{
						//Call the new replaceAll function, using the response document as the argument.
						CComVariant vtArg(sNewDoc.Copy());
						DISPPARAMS params = {&vtArg,0,1,0};
						DISPID dispid = 0;
						CComVariant vtResult;
						CComBSTR sReplaceAll(L"replaceAll");
						hr = InvokeDispatch(pDisp,sReplaceAll,&params,&vtResult,dispid);
					}
				}
			}
		}
	}
	catch(...){ASSERT(0);}
	return hr;


	/*
	 //This can't work, see KB problem Q272760
		if(pDoc2)
		{
			// write to the document
			//lifted code from MSDN
				VARIANT *param = 0;
				SAFEARRAY *sfArray = 0;

				// Creates a new one-dimensional array
				sfArray = SafeArrayCreateVector(VT_VARIANT, 0, 1);

				SafeArrayAccessData(sfArray,(LPVOID*) & param);
				param->vt = VT_BSTR;
				param->bstrVal = sNewDoc.Copy();
				SafeArrayUnaccessData(sfArray);
				pDoc2->write(sfArray);
				::SysFreeString(param->bstrVal);

				//END: lifted code.
			//close it to allow documentReady to be called
			pDoc2->close();
		}
		return hr;
		*/
}

// Add for Bug 348. The initialisation need to be done earlier then DocumentReady
// to make it possible to submit on the early events such as xforms-model-construct-done
// or xforms-ready etc.
HRESULT CImplSubmitInfo::initMethod(void)
{
	//set up the variables for method of sending and serialisation
	//These are the default configurations that can be set up
	//		using the method attribute as in the spec.

	// Bug 575 - Only set the mediatype if it is not specified.
	bool bSetMediaType = true;
	if(m_sMediaType != "")
		bSetMediaType = false;

	if(m_sMethod == L"post")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "post";
		if(m_sMediaType == "application/x-www-form-urlencoded")
			m_eSerialisation = nvpair;
		else
			m_eSerialisation = xml;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "application/xml";
	}
	else if(m_sMethod == L"form-data-post")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "post";
		m_eSerialisation = multipartForm;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "multipart/form-data";
	}
	else if(m_sMethod == L"multipart-post")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "post";
		m_eSerialisation = multipartRelated;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "multipart/related";
	}
	else if(m_sMethod == L"put")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "put";
		m_eSerialisation = xml;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "application/xml";
	}
	else if(m_sMethod == L"get")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "get";
		m_eSerialisation = nvpair;
		m_eSerialisedLocation = url;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "application/x-www-form-urlencoded";
	}
	else if(m_sMethod == L"urlencoded-post" ||  m_sMethod == L"url-encoded-post")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "post";
		m_eSerialisation = nvpair;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "application/x-www-form-urlencoded";
	}
	else if(m_sMethod == L"text-xml-post")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "post";
		m_eSerialisation = xml;
		m_eSerialisedLocation = body;	
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "text/xml";
	}
	else if(m_sMethod == L"delete")
	{
		if(!m_sRealMethod)
			m_sRealMethod = "delete";
		m_eSerialisation = xml;
		m_eSerialisedLocation = body;
		// Bug 575 - Only set the mediatype if it is not specified.
		if(bSetMediaType)
			m_sMediaType = "application/xml";
	}
	else
	{
		// Bug 650 - Indicate that the method specified is not supported.
		//m_bValidMethod = false;
	}

	return S_OK;
}

HRESULT CImplSubmitInfo::getIStream(IStream ** pStream) const
{
	HRESULT hr = S_OK;

	CComPtr<IStream> pNewStream = 0;
	// Create a stream
	hr = CreateStreamOnHGlobal(NULL, TRUE, &pNewStream);

	if(SUCCEEDED(hr))
		hr = pNewStream.QueryInterface(pStream);

	return S_OK;
}

HRESULT CImplSubmitInfo::BinaryFromVariant(BYTE ** ppBytes, ULONG * pulBuffSize, VARIANT * pvtData)
{
	HRESULT hr = S_OK;

	if(pvtData->vt == VT_DISPATCH && pvtData->pdispVal) {
		// If the variant contains a IDispatch pointer 
		// then get a pointer the IStream interface if 
		// available. 
		CComQIPtr<IStream> pStream = pvtData->pdispVal;
        if(pStream)
			// The read the data from it to the 
			// provided buffer.
			hr = BinaryFromIStream(ppBytes, pulBuffSize, pStream);
	} else if(pvtData->vt == VT_UNKNOWN && pvtData->punkVal) {
		// If the variant contains a IUnknown pointer 
		// then get a pointer the IStream interface if 
		// available. 
		CComQIPtr<IStream> pStream = pvtData->pdispVal;
		if(pStream)
			// The read the data from it to the 
			// provided buffer.
			hr = BinaryFromIStream(ppBytes, pulBuffSize, pStream);
	} else if(pvtData->vt == (VT_ARRAY | VT_UI1) && pvtData->parray ) {
		// The other type of variant we can get the data
		// from is an array.
		BYTE * pArrayData = NULL;
		// Get hold of a pointer to the data held 
		// by the safe array
		hr = SafeArrayAccessData(pvtData->parray, (void **) &pArrayData);
		if(SUCCEEDED(hr) && pArrayData) {
			// Allocate a buffer to hold the data
			*ppBytes	 = new BYTE[pvtData->parray->rgsabound->cElements];
			if(*ppBytes) {
				// If successful copy the number of bytes and
				// copy a the to the new buffer.
				*pulBuffSize = pvtData->parray->rgsabound->cElements;
				memcpy(*ppBytes, pArrayData, *pulBuffSize);
			} else {
				hr = E_OUTOFMEMORY;
			}
		}
		// Release the buffer.
		SafeArrayUnaccessData(pvtData->parray);
	}

	return S_OK;
}

HRESULT CImplSubmitInfo::BinaryFromIStream(BYTE ** ppBytes, ULONG * pulBuffSize, IStream * pStream)
{
	HRESULT hr = S_OK;

	if(pStream && ppBytes && pulBuffSize) {
		// Parameters to control the allocation
		// and reallocation of the data buffer.
		ULONG MIN_AVAIL_BUFF_SIZE = 512;
		ULONG MIN_ALLOC_BUFF_SIZE = 56000;
		// Create the inital buffer and initalise 
		// the size counts.
		BYTE * pBuffer = new BYTE[MIN_ALLOC_BUFF_SIZE];
		ULONG ulCurrSize = MIN_ALLOC_BUFF_SIZE, ulRead = 0, ulTotalRead = 0;
		// Keep reading from the stream until all 
		// the data has been read from it.
		while(SUCCEEDED(pStream->Read(pBuffer + ulTotalRead, ulCurrSize - ulTotalRead, &ulRead)) && ulRead) {
			// Check track of the actual amount of 
			// data read.
			ulTotalRead += ulRead;
			// If the buffers getting low then realloc it.
			if((ulCurrSize - ulTotalRead) <= MIN_AVAIL_BUFF_SIZE) {
				// Alloc a laregr buffer to hold the buffers
				BYTE * pTempBuffer = new BYTE[ulCurrSize + MIN_ALLOC_BUFF_SIZE];
				// On error report it
				if(!pTempBuffer) {
					hr = E_OUTOFMEMORY;
					break;
				}
				// Copy the data from the original buffer
				// to the new larger buffer.
				memcpy(pTempBuffer, pBuffer, ulCurrSize);
				// delete the old buffer.
				delete [] pBuffer;
				// Increase the count of the buffer size
				ulCurrSize = ulCurrSize + MIN_ALLOC_BUFF_SIZE;
				// point the new buffer to the pointer
				pBuffer = pTempBuffer;
			}
		}

		// If the data was successfully read
		// set the return values.
		if(SUCCEEDED(hr) && ulTotalRead) {
			*ppBytes		= pBuffer;
			*pulBuffSize	= ulTotalRead;
		} else {
			// On error we should clear the buffer.
			delete [] pBuffer;
			pBuffer = NULL;
		}
	} else {
		// We need a valid IStream to retrieve
		// the data from.
		hr = E_INVALIDARG;
	}

	return hr;
}

HRESULT CImplSubmitInfo::getSubmissionExtension(CSubmissionExtension ** ppSubExt)
{
	HRESULT hr = S_FALSE;
	if(ppSubExt) {
		// Copy the properties of the submission
		*ppSubExt = new CSubmissionExtension();
		if(*ppSubExt) {
			// Obtain the extension associated with the submission
			// if one exists.
			CComQIPtr<IXMLDOMNode> pExtension;
			getSubmissionExtensionElement(&pExtension);
			if(pExtension) {
				CComPtr<IXPathEvaluator> pEval;
				m_pModel->GetEvaluator(&pEval);
				(*ppSubExt)->putref_evaluator(pEval);
				(*ppSubExt)->SetTemplate(pExtension);
				CComPtr<IXMLDOMNode> pNode;

				CNode * pContext = 0;//TODO: probable leak here - pContext might not be released.
				hr = m_pModel->GetDefaultEvaluationContext(&pContext);
				if(SUCCEEDED(hr)) {
					if(m_sBind)
					{
						CImplBind *pBind = 0;
						m_pXForms->getBind(m_sBind,pBind);
						if(pBind)
						{
							CVertex * pVx = pBind->m_instVertices.GetAt(0);
							pVx->m_pInstanceNode->getNode(&pNode);
						}
					}
					else
					{
						m_pXForms->putXPathIdentityResolver(GetIdResolver());
						hr = m_pModel->getInstanceNode(m_sRef,pContext,false, &pNode);
                        m_pXForms->putXPathIdentityResolver(0);
					}

					// If we've not got a valid node then we should set it too 
					// the document element of the default instance for this xf:submission
					if(!pNode && m_pModel) {
						CImplInstance * pInst = m_pModel->getInstance();
						if(pInst) {
							CComPtr<IXMLDOMElement> pDocEl;
							CDOM3Document * pDomLive = pInst->GetLiveDOM();
							if(pDomLive)
								pDomLive->m_pXMLParser->get_documentElement(&pDocEl);
							if(pDocEl)
								pDocEl->QueryInterface(__uuidof(IXMLDOMNode), (void **) &pNode);
						}
					}

					hr = (*ppSubExt)->SetInstance(pNode);
				}
			}
		} else {
			hr = E_OUTOFMEMORY;
			// SetupError
		}
	} else {
		hr = E_INVALIDARG;
		// Setup Error Info
	}

	return hr;
}

void CImplSubmitInfo::AddHeader(BSTR sHeaderName,BSTR sValue)
{
	CImplContextualValue * v = new CImplContextualValue();
	v->m_sSimpleValue = sValue;
	delete  m_Headers[sHeaderName];
	m_Headers[sHeaderName] = v;
}

void CImplSubmitInfo::AddHeaderExpression(BSTR sHeaderName,BSTR sValueExpression)
{
	CImplContextualValue * v = new CImplContextualValue();
	v->m_sValueExpression = sValueExpression;
	delete  m_Headers[sHeaderName];
	m_Headers[sHeaderName] = v;
}

// HACK: This method has been added to work round a specific problem in Hubbub. It should be replaced
//       with an approach that fits better with the way that the rest of submission works (via XHR),
//       possibly exposing some of its details to form authors (e.g. usage of INTERNET_FLAG_NO_AUTH).
//       I've kept the function monolithic because it will be easier to remove that way (and it should
//       certainly be removed at some point).
HRESULT CImplSubmitInfo::wininetGet(BSTR sURL, CComBSTRMap &mapHeaders, VARIANT vData, VARIANT *pvResponse)
{
	HRESULT hr = S_OK;

	try
	{
		if(sURL)
		{
			// Open an internet connection.
			HINTERNET hInet = ::InternetOpenW(L"Ubiquity formsPlayer", INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
			if(hInet)
			{
				// Construct a single string of headers from the headers map.
				CComBSTR sHeaders;
				for(CComBSTRMap::iterator i = mapHeaders.begin(); i != mapHeaders.end(); ++i)
				{
					sHeaders += i->first;
					sHeaders += L": ";
					sHeaders += i->second;
					sHeaders += L"\r\n";
				}

				// Connect to sURL.
				HINTERNET hURL = ::InternetOpenUrlW(hInet, sURL, sHeaders, sHeaders.Length(), INTERNET_FLAG_NO_UI | INTERNET_FLAG_RESYNCHRONIZE | (m_bAuthenticate ? 0 : INTERNET_FLAG_NO_AUTH), 0);
				if(hURL)
				{
					// Check the connection is okay.
					DWORD dwCode, dwSize = sizeof(DWORD), dwIndex = 0;
					if(::HttpQueryInfo(hURL, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwSize, &dwIndex) == TRUE && dwCode == 200)
					{
						// Set up a buffer in which to store data that is read from the connection.
						static const int WININET_PACKET_SIZE = 4096;
						void *pBuffer = ::malloc(WININET_PACKET_SIZE);
						DWORD dwBytesRead = 0;
						bool bDone = false;
						while(!bDone && pBuffer)
						{
							// Read data from the connection.
							DWORD dw = 0;
							BOOL b = ::InternetReadFile(hURL, reinterpret_cast<BYTE*>(pBuffer) + dwBytesRead, WININET_PACKET_SIZE, &dw);
							dwBytesRead += dw;
							if(dw != WININET_PACKET_SIZE)
							{
								// All data has been read, perform a final zero-byte read to flush the cache.
								BYTE ignore;
								::InternetReadFile(hURL, &ignore, 1, &dw);
								bDone = true;
							}
							else
								// There is more data still to come, so increase the buffer size.
								pBuffer = ::realloc(pBuffer, dwBytesRead + WININET_PACKET_SIZE);
						}

						if(pBuffer)
						{
							*(reinterpret_cast<char *>(reinterpret_cast<BYTE *>(pBuffer) + dwBytesRead)) = '\0';

							// Check the encoding of the data in the buffer.
							bool bWideData = false;
							DWORD dwEncodingLength = 32, dwEncodingIndex = 0;
							char sEncoding[32 + 1];
							if(::HttpQueryInfo(hURL, HTTP_QUERY_CONTENT_TRANSFER_ENCODING, reinterpret_cast<void *>(sEncoding), &dwEncodingLength, &dwEncodingIndex) == TRUE && dwEncodingLength > 0)
							{
								sEncoding[dwEncodingLength] = '\0';
								if(strnicmp(sEncoding, "utf-16", 6) == 0 || strnicmp(sEncoding, "ucs-2", 5) == 0)
								{
									*(reinterpret_cast<wchar_t *>(reinterpret_cast<BYTE *>(pBuffer) + dwBytesRead)) = L'\0';
									bWideData = true;
								}
							}

							if(m_eReplaceType == Text)
							{
								CComVariant v;
								if(bWideData)
									v = reinterpret_cast<wchar_t *>(pBuffer);
								else
									v = reinterpret_cast<char *>(pBuffer);
								// Return the data as text.
								v.Detach(pvResponse);
							}
							else if(m_eReplaceType == Instance)
							{
								CImplInstance *pInstance = m_pInstance ? m_pInstance : m_pModel->getInstance();

								if(pInstance)
								{
									CComPtr<IXMLDOMDocument> pDoc;
									pInstance->getNewDocument(&pDoc);
									if(pDoc)
									{
										CComBSTR s;
										if(bWideData)
											s = reinterpret_cast<wchar_t *>(pBuffer);
										else
											s = reinterpret_cast<char *>(pBuffer);
										pDoc->put_async(VARIANT_FALSE);
										VARIANT_BOOL bLoaded = VARIANT_FALSE;
										pDoc->loadXML(s, &bLoaded);
										if(bLoaded == VARIANT_TRUE)
										{
											CComVariant v;
											v = pDoc;
											// Return the data as an XML DOM.
											v.Detach(pvResponse);
										}
										else
											hr = E_FAIL;
									}
									else
										hr = E_FAIL;
								}
								else
									hr = E_FAIL;
							}

							::free(pBuffer);
						}
						else
							hr = E_OUTOFMEMORY;
					}
					else
						hr = E_FAIL;

					::InternetCloseHandle(hURL);
				}
				else
					hr = E_FAIL;

				::InternetCloseHandle(hInet);
			}
			else
				hr = E_FAIL;
		}
		else
			hr = E_INVALIDARG;
	}
	catch(...)
	{
		hr = E_UNEXPECTED;
	}

	return hr;
}
